/**
 * According to the article "[35.13] How can I avoid linker errors with my template functions?":
 * http://www.parashift.com/c++-faq-lite/separate-template-fn-defn-from-decl.html
 */

#include <cuda_runtime.h>
#include <helper_cuda.h>

#include <log4cxx/logger.h>
#include <base/logging.h>
#include <base/types.cuh>
#include <base/methods.cuh>

namespace recognition {

double mypow(double a, int deg) {
	for (int i = 0; i < deg; ++i)
		a *= a;

	return a;
}

double GetDevMemoryUsedMb() {
	size_t free_bytes, total_bytes;
	checkCudaErrors( cudaMemGetInfo(&free_bytes, &total_bytes) );
	double free_d = (double) free_bytes;
	double total_d = (double) total_bytes;
	double used_d = total_d - free_d;

	return BYTES_TO_MB(used_d);
}

void ReportDevMemoryUsage(log4cxx::LoggerPtr logger, const char* func_name, double dev_mem_used_start) {
	static const char dev_memory_used_log_preffix[] = "GPU memory used in: ";
	double dev_mem_used_end = GetDevMemoryUsedMb();
	double dev_mem_used = dev_mem_used_end - dev_mem_used_start;
	size_t preffix_len = sizeof(dev_memory_used_log_preffix) - 1;
	size_t padder_len = sizeof(PADDER) - 1;
	size_t func_name_len = strlen(func_name);
	size_t pad_amount = padder_len - preffix_len - func_name_len;
	if ((int)pad_amount > 0) {
		LOG_DEBUG(logger, "%s%s%s[%8.3f MB] (Total: %8.3f MB)", dev_memory_used_log_preffix, func_name, PADDER + preffix_len + func_name_len,
				dev_mem_used, dev_mem_used_end);
	} else {
		LOG_DEBUG(logger, "%s%s [%8.3f MB] (Total: %8.3f MB)", dev_memory_used_log_preffix, func_name, dev_mem_used, dev_mem_used_end);
	}
}

void ReportMethodExecutionTime(log4cxx::LoggerPtr logger, const char* func_name, double time) {
	static const char method_time_log_preffix[] = "Time: ";
	size_t preffix_len = sizeof(method_time_log_preffix) - 1;
	size_t padder_len = sizeof(PADDER) - 1;
	size_t func_name_len = strlen(func_name);
	size_t pad_amount = padder_len - preffix_len - func_name_len;
	if ((int)pad_amount > 0) {
		LOG_DEBUG(logger, "%s%s%s[%.4f sec]", method_time_log_preffix, func_name, PADDER + preffix_len + func_name_len, time);
	} else {
		LOG_DEBUG(logger, "%s%s [%.4f sec]", method_time_log_preffix, func_name, time);
	}
}

void ReportKernelExecutionTime(log4cxx::LoggerPtr logger, const char* func_name, double time) {
	static const char kernel_time_log_preffix[] = "Kernel time in: ";
	size_t preffix_len = sizeof(kernel_time_log_preffix) - 1;
	size_t padder_len = sizeof(PADDER) - 1;
	size_t func_name_len = strlen(func_name);
	size_t pad_amount = padder_len - preffix_len - func_name_len;
	if ((int)pad_amount > 0) {
		LOG_DEBUG(logger, "%s%s%s[%.4f sec]", kernel_time_log_preffix, func_name, PADDER + preffix_len + func_name_len, time);
	} else {
		LOG_DEBUG(logger, "%s%s [%.4f sec]", kernel_time_log_preffix, func_name, time);
	}
}

}	// namespace recognition
